import * as THREE from "three"
import Matter from "matter-js"
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { Overlay } from "./Overlay";
import { Bus, CONN_ACTIVITY } from '../Bus';
import { worldToScreen } from "../tooler";
import { ViewMap } from "./ViewMap";
import { initConn } from "../Connection";

const Engine = Matter.Engine;
const Runner = Matter.Runner;
const Bodies = Matter.Bodies;
const Composite = Matter.Composite;
const Constraint = Matter.Constraint;

let overlay: Overlay;
let group: any;
let stats: any;
let camera: THREE.PerspectiveCamera, scene: THREE.Scene, renderer: THREE.WebGLRenderer;
let selectedObject: any;
let viewMap = new ViewMap();
const pointer = new THREE.Vector2();
const raycaster = new THREE.Raycaster();

export class App {
    static engine:Matter.Engine;
    static group:THREE.Group;

    constructor(canvas: any, dom: any) {
        overlay = new Overlay(dom);
        initPhysics();
        initStage(canvas);
        initConn();
    }
}

function initPhysics() {
    var engine = Engine.create();
    engine.gravity.y = 0;
    var runner = Runner.create();
    Runner.run(runner, engine);
    App.engine = engine;
}

function initStage(canvas: any) {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20000);
    camera.position.z = 1000;

    const controls = new OrbitControls(camera, document.body);
    controls.minDistance = 100;
    controls.maxDistance = 30000;
    controls.enableRotate = false;

    scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x000000, 0.0004);

    group = new THREE.Group();
    scene.add(group);

    renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    // renderer.autoClear = false;

    stats = new Stats();
    document.body.appendChild(stats.dom);

    let light = new THREE.DirectionalLight(0xffffff, 0.8);
    light.position.set(200, 200, 100);
    scene.add(light);

    App.group = group;

    addEvents();
    animate();
}

function addEvents() {
    window.addEventListener('resize', onWindowResize);
    document.addEventListener('pointermove', onPointerMove);
    document.addEventListener('click', onClick);

    Bus.on(CONN_ACTIVITY, obj => {
        // console.log("CONN_ACTIVITY", obj);
        viewMap.addData(obj);
    });
}

function animate() {
    requestAnimationFrame(animate);
    stats.update();
    viewMap.update();
    updateOverlayPosition();
    renderer.render(scene, camera);
}

function updateOverlayPosition() {
    if (!selectedObject) {
        overlay.hide();
        return;
    }
    const vector = worldToScreen(selectedObject, camera);
    const widthHalf = window.innerWidth / 2;
    const heightHalf = window.innerHeight / 2;
    const x = (vector.x * widthHalf) + widthHalf;
    const y = -(vector.y * heightHalf) + heightHalf;
    overlay.show(x, y);
}

function onClick(e: any) {
    let find = getHitObject(e);
    if (find) {
        selectedObject = find.mesh;
        let body = find.body;
        // Matter.Body.applyForce(body, { x: body.position.x, y: body.position.y }, { x: 0, y: 0.005 });
        // console.log(find, "find");
        overlay.setText(find.id)
    }
}

function getHitObject(event: any) {
    if (selectedObject) {
        // selectedObject.material.color.set(lastColor);
        selectedObject = null;
    }

    pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
    pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(pointer, camera);

    const intersects = raycaster.intersectObject(group, true);

    if (intersects.length > 0) {
        const res = intersects.filter(function (res) {
            return res && res.object;
        })[0];

        if (res && res.object) {
            return viewMap.getHit(res.object);
        }

    }
    return null;
}

function onPointerMove(e: any) {
    let find = getHitObject(e)
    if (find) {
        selectedObject = find.mesh;
        let body = find.body;
        // Matter.Body.applyForce(body, { x: body.position.x, y: body.position.y }, { x: 0, y: 0.005 });
        // console.log(find, "find");
        overlay.setText(find.id);
        overlay.show(body.position.x, body.position.y)
    }

}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}